1 /**
2  * License:
3  *              Copyright Devisualization (Richard Andrew Cattermole) 2014 - 2017.
4  *     Distributed under the Boost Software License, Version 1.0.
5  *        (See accompanying file LICENSE_1_0.txt or copy at
6  *              http://www.boost.org/LICENSE_1_0.txt)
7  */
8 module devisualization.util.core.linegraph;
9 
10 /**
11  * A line graph.
12  * Given points produces all points upon the line.
13  * 
14  * Does not support decimal (float) points.
15  * 
16  * Could be rather useful for Devisualization.Image filters perhaps?
17  */
18 deprecated("Use PointsGenerator instead.")
19 struct LineGraphType(bool checkNegative, TYPE) {
20     private {
21         enum GraphFunction {
22             SplineCurve,
23             BezierCurve,
24             Line,
25             Point
26         }
27 
28         TYPE[] coordsx;
29         TYPE[] coordsy;
30         GraphFunction[] functions;
31 
32         TYPE[][] xPointBezierCurve;
33         TYPE[][] yPointBezierCurve;
34         float[] splineCurveTensions;
35     }
36 
37     /**
38      * Initiate with starting point
39      * Acts as a lineTo
40      * 
41      * Params:
42      *         startx    =    Starting x coord
43      *         starty    =    Strating y coord
44      */
45     this(TYPE startx = 0, TYPE starty = 0) {
46         pointAt(startx, starty);
47     }
48 
49     /**
50      * Adds a straight line.
51      * 
52      * Params:
53      *         x    =    To position
54      *         y    =    To position
55      */
56     void lineTo(TYPE x, TYPE y) {
57         coordsx ~= x;
58         coordsy ~= y;
59         functions ~= GraphFunction.Line;
60     }
61 
62     /**
63      * Adds a posize_t line.
64      * 
65      * Params:
66      *         x    =    To position
67      *         y    =    To position
68      */
69     void pointAt(TYPE x, TYPE y) {
70         coordsx ~= x;
71         coordsy ~= y;
72         functions ~= GraphFunction.Point;
73     }
74 
75     /**
76      * Adds a bezier curve.
77      * 
78      * Params:
79      *         x        =    To position
80      *         y        =    To position
81      *         xPoints  =    Points to manipulate by
82      *         yPoints  =    Points to manipulate by
83      */
84     void bezierCurveTo(TYPE x, TYPE y, TYPE[] xPoints, TYPE[] yPoints) {
85         coordsx ~= x;
86         coordsy ~= y;
87         functions ~= GraphFunction.BezierCurve;
88         xPointBezierCurve ~= xPoints;
89         yPointBezierCurve ~= yPoints;
90     }
91 
92     /**
93      * Adds a bezier curve.
94      * 
95      * Params:
96      *         x    =    To position
97      *         y    =    To position
98      *         x2   =    Point to manipulate by
99      *         y2   =    Point to manipulate by
100      */
101     void bezierCurveTo(TYPE x, TYPE y, TYPE x2, TYPE y2) {
102         coordsx ~= x;
103         coordsy ~= y;
104         functions ~= GraphFunction.BezierCurve;
105         xPointBezierCurve ~= [x2];
106         yPointBezierCurve ~= [y2];
107     }
108     
109     /**
110      * Adds a bezeier curve using points to derive the control points
111      * 
112      * Params:
113      *         x        =    To position
114      *         y        =    To position
115      *         x2       =    Point to go through
116      *         y2       =    Point to go through
117      *         tension  =    Tension for the curve
118      */
119     void splineThrough(TYPE x, TYPE y, TYPE x2, TYPE y2, float tension) {
120         coordsx ~= x;
121         coordsy ~= y;
122         functions ~= GraphFunction.SplineCurve;
123         xPointBezierCurve ~= [x2];
124         yPointBezierCurve ~= [y2];
125         splineCurveTensions ~= tension;
126     }
127 
128     /**
129      * Get all points upon the line
130      * 
131      * Params:
132      *         del     =    Delegate to call with a point
133      *         step    =    The difference between points. Default: 1
134      */
135     void apply(void delegate(TYPE x, TYPE y) del, size_t step = 1) {
136         void d(TYPE x, TYPE y, float w) {
137             del(x, y);
138         }
139 
140         applyFunc!(checkNegative, TYPE)(this, &d, step);
141     }
142 
143     /**
144      * Get all points upon the line
145      * Includes weighting for the point. Ranges from 0f .. 1f
146      * 
147      * Params:
148      *         del     =    Delegate to call with a point
149      *         step    =    The difference between points. Default: 1
150      */
151     void apply(void delegate(TYPE x, TYPE y, float weighting) del, size_t step = 1) {
152         applyFunc!(checkNegative, TYPE)(this, del, step);
153     }
154 }
155 
156 alias LineGraph = LineGraphType!(true, size_t);
157 alias SignedLineGraph = LineGraphType!(false, ptrdiff_t);
158 
159 private {
160     void applyFunc(bool checkNeg, TYPE, GRAPH)(GRAPH graph, void delegate(TYPE x, TYPE y, float w) del, size_t step = 1) {
161         import std.math : floor, ceil, sin, cos, sqrt, PI, abs, pow;
162         import std.algorithm : swap;
163 
164         with(graph) {
165             TYPE lastx = coordsx[0];
166             TYPE lasty = coordsy[0];
167             size_t currentBezierCurve, currentSpline;
168             del(lastx, lasty, 1f);
169 
170             foreach(i, func; functions) {
171                 TYPE x = coordsx[i];
172                 TYPE y = coordsy[i];
173                 
174                 TYPE newx = lastx;
175                 TYPE newy = lasty;
176 
177                 if (func == GraphFunction.Line) {
178                     // http://en.wikipedia.org/wiki/Xiaolin_Wu%27s_line_algorithm
179                     // modified to force the start and end values to be 100% weighting
180 
181                      void plot(TYPE xx, TYPE yy, float w) {
182                         // is pixel in range?
183                         // algo can go outside
184 						// FIXME:
185                         /*if (y > lasty && yy <= y && yy >= lasty) {
186                             if (x > lastx && xx <= x && xx >= lastx) {
187                                 del(xx, yy, w);
188                             }
189                         }*/
190 						del(xx, yy, w);
191                     }
192 
193                     TYPE ipart(float x) { return cast(TYPE)x; } // integer part of x
194                     TYPE round(float x) { return ipart(x + 0.5); }
195                     float fpart(float x) { return (x < 0) ?
196                         (1f - (x - x.floor)) :
197                         (x - x.floor); } // fractional part of x
198                     float rfpart(float x) { return 1 - fpart(x); }
199 
200                     void drawLine(TYPE x0, TYPE y0, TYPE x1, TYPE y1) {
201                         bool steep = abs(y1 - y0) > abs(x1 - x0);
202                         if (steep) {
203                             swap(x0, y0);
204                             swap(x1, y1);
205                         }
206                         if (x0 > x1) {
207                             swap(x0, x1);
208                             swap(y0, y1);
209                         }
210 
211                         TYPE dx = x1 - x0;
212                         TYPE dy = y1 - y0;
213                         float gradient = (cast(float)dy / cast(float)dx);
214 
215                         // handle first endpoint
216                         TYPE xend = round(x0);
217                         float yend = y0 + gradient * (xend - x0);
218                         float xgap = rfpart(x0 + 0.5f);
219                         TYPE xpxl1 = xend; // this will be used in the main loop
220                         TYPE ypxl1 = ipart(yend);
221                         if (steep) {
222                             //plot(ypxl1, xpxl1, 1f); // doubled up
223                             plot(ypxl1+1, xpxl1, fpart(yend) * xgap);
224                         } else {
225                             //plot(xpxl1, ypxl1, 1f); // doubled up
226                             plot(xpxl1, ypxl1+1,  fpart(yend) * xgap);
227                         }
228                         float intery = yend + gradient; // first y-intersection for the main loop
229 
230                         // handle second endpoint
231                         xend = round(x1);
232                         yend = y1 + gradient * (xend - x1);
233                         xgap = fpart(x1 + 0.5);
234                         TYPE xpxl2 = xend; //this will be used in the main loop
235                         TYPE ypxl2 = ipart(yend);
236                         if (steep) {
237                             plot(ypxl2, xpxl2, 1f);
238                             plot(ypxl2+1, xpxl2,  fpart(yend) * xgap);
239                         } else {
240                             plot(xpxl2, ypxl2,  rfpart(yend) * xgap);
241                             plot(xpxl2, ypxl2+1, 2f);
242                         }
243 
244                         // main loop
245                         foreach(x; xpxl1 + 1 .. xpxl2) {
246                             if (steep) {
247                                 plot(ipart(intery), x, rfpart(intery));
248                                 plot(ipart(intery)+1, x, fpart(intery));
249                             } else {
250                                 plot(x, ipart(intery), rfpart(intery));
251                                 plot(x, ipart(intery)+1, fpart(intery));
252                             }
253                             intery += gradient * step;
254                         }
255                     }
256 
257                     drawLine(lastx, lasty, x, y);
258                     lastx = x;
259                     lasty = y;
260                 } else if (func == GraphFunction.Point) {
261                     del(x, y, 1f);
262                     lastx = x;
263                     lasty = y;
264                 } else if (func == GraphFunction.BezierCurve || func == GraphFunction.SplineCurve) {
265                     //heavily based upon http://www.codeproject.com/Articles/25237/Bezier-Curves-Made-Simple
266 
267                     TYPE[] xPoints = xPointBezierCurve[currentBezierCurve];
268                     TYPE[] yPoints = yPointBezierCurve[currentBezierCurve];
269                     
270                     if (func == GraphFunction.SplineCurve) {
271                         TYPE[] xPoints2;
272                         TYPE[] yPoints2;
273                         xPoints2.length = xPoints.length + 1;
274                         yPoints2.length = yPoints.length + 1;
275                         
276                         float tension = splineCurveTensions[currentSpline];
277                         
278                         size_t poi;
279                         foreach(j, xp; xPoints) {
280                             TYPE yp = yPoints[j];
281                             TYPE xpp, ypp;
282                             
283                             if (j == xPoints.length - 1) {
284                                 xpp = x;
285                                 ypp = y;
286                             } else {
287                                 xpp = x;
288                                 ypp = y;
289                             }
290                                 
291                             auto splineControlPoints = getSplineControlPoints(lastx, lasty, xp, yp, xpp, ypp, tension);
292                             xPoints2[poi] = splineControlPoints[0];
293                             yPoints2[poi] = splineControlPoints[1];
294                             xPoints2[poi + 1] = splineControlPoints[2];
295                             yPoints2[poi + 1] = splineControlPoints[3];
296                             
297                             poi += 2;
298                         }
299                         
300                         xPoints = [lastx] ~ xPoints2 ~ [x];
301                         yPoints = [lasty] ~ yPoints2 ~ [y];
302                         currentSpline++;
303                     } else {
304                         xPoints = [lastx] ~ xPoints ~ [x];
305                         yPoints = [lasty] ~ yPoints ~ [y];
306                     }
307 
308                     currentBezierCurve++;
309 
310                     double Ni(size_t n, size_t i) {
311                         import devisualization.util.core.math : factorial;
312 
313                         double ni;
314                         double a1 = factorial(n);
315                         double a2 = factorial(i);
316                         double a3 = factorial(n - i);
317                         ni = a1 / (a2 * a3);
318                         return ni;
319                     }
320 
321                     double Bernstein(size_t n, size_t i, double t) {
322                         double basis;
323                         double ti; /* t^i */
324                         double tni; /* (1 - t)^i */
325                           
326                         /* Prevent problems with pow */
327                                   
328                         if (t == 0.0 && i == 0) 
329                             ti = 1.0; 
330                         else 
331                             ti = pow(t, i);
332                           
333                         if (n == i && t == 1.0) 
334                             tni = 1.0; 
335                         else 
336                             tni = pow((1 - t), (n - i));
337                           
338                         //Bernstein basis
339                         basis = Ni(n, i) * ti * tni; 
340                         return basis;
341                     }
342 
343                     void Bezier2D(size_t cpts) {
344                         size_t npts = xPoints.length;
345                         double t;
346                         double step2 = 1f / (cpts - 1);
347 
348                         // Calculate points on curve
349                                   
350                         t = 0;
351                                       
352                         for (size_t j = 0; j < cpts; j++) { 
353                             if ((1.0 - t) < 5e-6) 
354                                 t = 1.0;
355                                   
356                             double xout = 0;
357                             double yout = 0;
358 
359                             for (size_t k = 0; k < npts; k++) {
360                                 double basis = Bernstein(npts - 1, k, t);
361                                 xout += basis * xPoints[k];
362                                 yout += basis * yPoints[k];
363                             }
364 
365                             if (!(newx == cast(TYPE)xout && newy == cast(TYPE)yout))
366                                 del(cast(TYPE)xout, cast(TYPE)yout, 1f);
367                             newx = cast(TYPE)xout;
368                             newy = cast(TYPE)yout;
369 
370                             t += step2;
371                         }
372                     }
373 
374                     size_t xPointsS;
375                     if (lastx > x)
376                         xPointsS = lastx - x;
377                     else
378                         xPointsS = x - lastx;
379                     size_t yPointsS;
380                     if (lasty > y)
381                         yPointsS = lasty - y;
382                     else
383                         yPointsS = y - lasty;
384                     Bezier2D(xPointsS * yPointsS);
385 
386                     lastx = x;
387                     lasty = y;
388                 }
389             }
390         }
391     }
392 
393     TYPE[4] getSplineControlPoints(TYPE)(TYPE x0, TYPE y0, TYPE x1, TYPE y1, TYPE x2, TYPE y2, float t){
394         // from http://scaledinnovation.com/analytics/splines/aboutSplines.html
395         import std.math : sqrt, pow;
396         
397         real d01 = sqrt(pow(x1 - x0, 2f) + pow(y1 - y0, 2f));
398         real d12 = sqrt(pow(x2 - x1, 2f) + pow(y2 - y1, 2f));
399         real fa = t * d01 / (d01 + d12);                   // scaling factor for triangle Ta
400         real fb = t * d12 / (d01 + d12);                   // ditto for Tb, simplifies to fb=t-fa
401         TYPE p1x = cast(TYPE)(x1 - fa * (x2 - x0));        // x2-x0 is the width of triangle T
402         TYPE p1y = cast(TYPE)(y1 - fa * (y2 - y0));        // y2-y0 is the height of T
403         TYPE p2x = cast(TYPE)(x1 + fb * (x2 - x0));
404         TYPE p2y = cast(TYPE)(y1 + fb * (y2 - y0));  
405         return [p1x, p1y, p2x, p2y];
406     }
407 }
408 
409 /**
410  * A line graph.
411  * Given points produces all points upon the line.
412  * 
413  * Uses the GC.
414  */
415 struct PointsGenerator(Point) if (__traits(compiles, {
416 			Point p;
417 			double
418 				x = p.x,
419 				y = p.y,
420 				z = p.z;
421 			p.x = x;
422 			p.y = y;
423 			p.z = z;
424 	})) {
425 
426 	private {
427 		Segment!Point[] segments;
428 		Point startAt;
429 	}
430 	
431 	this(Point startAt) {
432 		import std.math : isNaN;
433 		
434 		if (startAt.x.isNaN)
435 			startAt.x = 0;
436 		if (startAt.y.isNaN)
437 			startAt.y = 0;
438 		if (startAt.z.isNaN)
439 			startAt.z = 0;
440 		
441 		this.startAt = startAt;
442 	}
443 	
444 	void pointAt(Point location) {
445 		import std.math : isNaN;
446 		
447 		if (location.x.isNaN)
448 			location.x = 0;
449 		if (location.y.isNaN)
450 			location.y = 0;
451 		if (location.z.isNaN)
452 			location.z = 0;
453 		
454 		segments ~= Segment!Point(Segment!Point.Type.Point, location);
455 	}
456 	
457 	void lineTo(Point location) {
458 		import std.math : isNaN;
459 		
460 		if (location.x.isNaN)
461 			location.x = 0;
462 		if (location.y.isNaN)
463 			location.y = 0;
464 		if (location.z.isNaN)
465 			location.z = 0;
466 		
467 		segments ~= Segment!Point(Segment!Point.Type.LineTo, location);
468 	}
469 	
470 	void bezierOf(Point location, Point[] control...) {
471 		if (control.length == 0) {
472 			// basically is a straight line,
473 			// but bezier curve evaluation is slow compared to lineTo.
474 			lineTo(location);
475 			// hence we switch to it instead.
476 		} else {
477 			segments ~= Segment!Point(Segment!Point.Type.Bezier, location);
478 			segments[$-1].bezier_points = control.dup;
479 		}
480 	}
481 	
482 	void arcOf(Point location, Point arc_radius) {
483 		segments ~= Segment!Point(Segment!Point.Type.Arc, location);
484 		segments[$-1].arc_radius = arc_radius;
485 	}
486 	
487 	auto generate(double interval) {
488 		struct Result {
489 			Segment!Point[] segments;
490 			immutable(double) interval;
491 			Point next, current;
492 			
493 			this(Segment!Point[] segments, double interval, Point startAt) {
494 				this.segments = segments;
495 				this.interval = interval;
496 				next = startAt;
497 				
498 				if (segments.length > 0)
499 					segments[0].initialize(interval, next);
500 				next = startAt;
501 			}
502 			
503 			@property {
504 				pragma(inline, true)
505 				Point front() {
506 					return next;
507 				}
508 				
509 				pragma(inline, true)
510 				bool empty() {
511 					return segments.length == 0 || next == Point.init;
512 				}
513 			}
514 			
515 			void popFront() {
516 				import std.math : isNaN;
517 				current = next;
518 				next = Point.init;
519 				
520 				while(segments.length > 0) {
521 					Point ret = segments[0].nextCoord(interval, current);
522 					
523 					if (ret.x.isNaN || ret.y.isNaN || ret.z.isNaN) {
524 						segments = segments[1 .. $];
525 						if (segments.length > 0)
526 							segments[0].initialize(interval, current);
527 					} else {
528 						next = ret;
529 						break;
530 					}
531 				}
532 			}
533 		}
534 		
535 		return Result(segments.dup, interval, startAt);
536 	}
537 }
538 
539 private {
540 	struct Segment(Point) {
541 		Type type;
542 		Point location, arc_radius;
543 		Point[] bezier_points;
544 		
545 		void initialize(double interval, ref Point previous) {
546 			import std.math : abs, copysign, pow, PI, PI_2, sqrt;
547 			
548 			switch(type) {
549 				case Type.LineTo:
550 					dx = location.x - previous.x;
551 					dy = location.y - previous.y;
552 					dz = location.z - previous.z;
553 					
554 					Point point_abs = Point(abs(dx), abs(dy), abs(dz));
555 					double point_sum_third = (point_abs.x + point_abs.y + point_abs.z) * (1/3f);
556 					Point point_portion = Point(
557 						point_abs.x / point_sum_third,
558 						point_abs.y / point_sum_third,
559 						point_abs.z / point_sum_third);
560 					
561 					lineTo_delta = Point(
562 						copysign(point_portion.x, dx) * interval,
563 						copysign(point_portion.y, dy) * interval,
564 						copysign(point_portion.z, dz) * interval);
565 					break;
566 					
567 				case Type.Arc:
568 					// This code is surprisingly like lineTo.
569 					
570 					// Required to calculate starting angles (to the quartent) of the curve.
571 					//  in lineTo it only needs 'effective' difference, where as here we
572 					//  need full correct difference with understanding of negatives.
573 					dx = calculateDifferenceOnAxis(location.x, previous.x);
574 					dy = calculateDifferenceOnAxis(location.y, previous.y);
575 					dz = calculateDifferenceOnAxis(location.z, previous.z);
576 					
577 					arc_center = Point(
578 						(location.x + previous.x)/2,
579 						(location.y + previous.y)/2,
580 						(location.z + previous.z)/2);
581 					arc_radius = Point(
582 						abs(arc_center.x-location.x),
583 						abs(arc_center.y-location.y),
584 						abs(arc_center.z-location.z));
585 					
586 					double train_error1, train_error2,
587 						current_angle1, current_angle2;
588 					
589 					// x
590 					
591 					if (dx > 0) {
592 						current_angle1 = PI;
593 						current_angle2 = PI+PI_2;
594 					} else {
595 						current_angle1 = 0;
596 						current_angle2 = PI_2;
597 					}
598 					
599 					train_error1 = 0;
600 					train_error2 = 0;
601 					
602 					while(train_NN(current_angle1, train_error1, 0.1, arc_radius.x, arc_center.x, previous.x)) {}
603 					while(train_NN(current_angle2, train_error2, 0.1, arc_radius.x, arc_center.x, location.x)) {}
604 					
605 					if (current_angle1 > current_angle2 && dx > 0) {
606 						arc_angle.x = (2*PI) - current_angle1;
607 						arc_maxangle.x = (2*PI) - current_angle2;
608 					} else {
609 						arc_angle.x = current_angle1;
610 						arc_maxangle.x = current_angle2;
611 					}
612 					
613 					// x
614 					// y
615 					
616 					if (dy > 0) {
617 						current_angle1 = PI;
618 						current_angle2 = PI+PI_2;
619 					} else {
620 						current_angle1 = 0;
621 						current_angle2 = PI_2;
622 					}
623 					
624 					train_error1 = 0;
625 					train_error2 = 0;
626 					
627 					while(train_NN(current_angle1, train_error1, 0.1, arc_radius.y, arc_center.y, previous.y)) {}
628 					while(train_NN(current_angle2, train_error2, 0.1, arc_radius.y, arc_center.y, location.y)) {}
629 					
630 					if (current_angle1 > current_angle2 && dy > 0) {
631 						arc_angle.y = (2*PI) - current_angle1;
632 						arc_maxangle.y = (2*PI) - current_angle2;
633 					} else {
634 						arc_angle.y = current_angle1;
635 						arc_maxangle.y = current_angle2;
636 					}
637 					
638 					// y
639 					// z
640 					
641 					if (dz > 0) {
642 						current_angle1 = PI;
643 						current_angle2 = PI+PI_2;
644 					} else {
645 						current_angle1 = 0;
646 						current_angle2 = PI_2;
647 					}
648 					
649 					train_error1 = 0;
650 					train_error2 = 0;
651 					
652 					while(train_NN(current_angle1, train_error1, 0.1, arc_radius.z, arc_center.z, previous.z)) {}
653 					while(train_NN(current_angle2, train_error2, 0.1, arc_radius.z, arc_center.z, location.z)) {}
654 					
655 					if (current_angle1 > current_angle2 && dz > 0) {
656 						arc_angle.z = (2*PI) - current_angle1;
657 						arc_maxangle.z = (2*PI) - current_angle2;
658 					} else {
659 						arc_angle.z = current_angle1;
660 						arc_maxangle.z = current_angle2;
661 					}
662 					
663 					// z
664 					
665 					double angle_sum_third = (abs(dx)+abs(dy)+abs(dz))*(1/3f);
666 					Point angle_diff = Point(
667 						arc_maxangle.x-arc_angle.x,
668 						arc_maxangle.y-arc_angle.y,
669 						arc_maxangle.z-arc_angle.z);
670 					arc_delta = Point(
671 						copysign(angle_diff.x / angle_sum_third, angle_diff.x) * interval,
672 						copysign(angle_diff.y / angle_sum_third, angle_diff.y) * interval,
673 						copysign(angle_diff.z / angle_sum_third, angle_diff.z) * interval);
674 					
675 					arc_angle.x += arc_delta.x;
676 					arc_angle.y += arc_delta.y;
677 					arc_angle.z += arc_delta.z;
678 					break;
679 					
680 				case Type.Bezier:
681 					bezier_buffer.length = bezier_points.length+2;
682 					origin_point = previous;
683 					
684 					// Our goal for getting the number of ticks to operate for
685 					//  is to determine essentially the length of the line.
686 					// Unfortunately this goal is impossible to do.
687 					// So we do things a bit more evilly.
688 					
689 					// And truth is, I don't care too much about it.
690 					// What I care about is figuring out a tick count that will work for all of the arc's.
691 					
692 					// Each bezier curve exists in a 0f..1f space.
693 					// But we have three axis to work upon, not two.
694 					// What we want to archive is to get the difference between each control point,
695 					//  is to be roughly the length of interval.
696 					// Because of this, we treat each arc seperately,
697 					//  the distance we want, is (bestDistance/arcs.length) == interval
698 					//  we know that it won't be possible to get this to occur,
699 					//  but we want to get as close to it as we can.
700 					
701 					double
702 						bestDistance = double.max_exp,
703 						bestTickCount = 1,
704 						expectedDistance = interval*(bezier_points.length+1),
705 						tickCount=1;
706 					
707 					foreach(i; 0 .. 60) {
708 						// 0..4 = 1
709 						// 4..8 = 0.1
710 						// 8..12 = 0.01
711 						double
712 							learnRate =
713 								i < 20 ? interval :
714 								i < 40 ? interval / 10 :
715 								interval / 100;
716 						Point sum = Point(0, 0, 0);
717 						
718 						foreach(j; 0 .. 2) {
719 							double tickIndevidual = 1f/tickCount;
720 							double tick = j == 0 ? tickIndevidual : (2f/tickCount);
721 							Point startPoint = previous;
722 							
723 							foreach(p; bezier_points) {
724 								sum.x += bezier_combine(startPoint.x, p.x, tick+tickIndevidual)-bezier_combine(startPoint.x, p.x, tick);
725 								sum.y += bezier_combine(startPoint.y, p.y, tick+tickIndevidual)-bezier_combine(startPoint.y, p.y, tick);
726 								sum.z += bezier_combine(startPoint.z, p.z, tick+tickIndevidual)-bezier_combine(startPoint.z, p.z, tick);
727 								
728 								startPoint = p;
729 							}
730 							
731 							sum.x += bezier_combine(startPoint.x, location.x, tick+tickIndevidual)-bezier_combine(startPoint.x, location.x, tick);
732 							sum.y += bezier_combine(startPoint.y, location.y, tick+tickIndevidual)-bezier_combine(startPoint.y, location.y, tick);
733 							sum.z += bezier_combine(startPoint.z, location.z, tick+tickIndevidual)-bezier_combine(startPoint.z, location.z, tick);
734 						}
735 						
736 						double sumV = (sum.x+sum.y+sum.z)/6;
737 						if (bestDistance - expectedDistance > sumV-expectedDistance) {
738 							bestDistance = sumV;
739 							bestTickCount = tickCount;
740 						} else {
741 							tickCount += tickCount * learnRate * (sumV / expectedDistance);
742 						}
743 					}
744 					
745 					bezier_tick = 1f/(bestTickCount/2);
746 					bezier_bezierOffset = bezier_tick;
747 					break;
748 					
749 				default:
750 					break;
751 			}
752 		}
753 		
754 		double dx, dy, dz;
755 		Point origin_point;
756 		union {
757 			struct {
758 				double[] bezier_buffer;
759 				double bezier_tick,
760 					bezier_bezierOffset;
761 			}
762 			
763 			struct {
764 				Point arc_delta,
765 					arc_center,
766 					arc_maxangle,
767 					arc_angle;
768 			}
769 			Point lineTo_delta;
770 		}
771 		
772 		Point nextCoord(double interval, ref Point previous) {
773 			switch(type) {
774 				case Type.Point:
775 					scope(exit) location = Point.init;
776 					return location;
777 					
778 				case Type.LineTo:
779 					if ((dx > 0 && previous.x >= location.x) || (dx < 0 && previous.x <= location.x) ||
780 						(dy > 0 && previous.y >= location.y) || (dy < 0 && previous.y <= location.y) ||
781 						(dz > 0 && previous.z >= location.z) || (dz < 0 && previous.z <= location.z))
782 						return Point.init;
783 					else {
784 						Point ret = Point(
785 							previous.x + lineTo_delta.x,
786 							previous.y + lineTo_delta.y,
787 							previous.z + lineTo_delta.z);
788 						
789 						if ((dx > 0 && ret.x >= location.x) || (dx < 0 && ret.x <= location.x))
790 							ret.x = location.x;
791 						if ((dy > 0 && ret.y >= location.y) || (dy < 0 && ret.y <= location.y))
792 							ret.y = location.y;
793 						if ((dz > 0 && ret.z >= location.z) || (dz < 0 && ret.z <= location.z))
794 							ret.z = location.z;
795 						
796 						return ret;
797 					}
798 					
799 				case Type.Arc:
800 					import std.math : cos, sin;
801 					
802 					if (arc_angle.x >= arc_maxangle.x ||
803 						arc_angle.y >= arc_maxangle.y ||
804 						arc_angle.z >= arc_maxangle.z	)
805 						return Point.init;
806 					
807 					Point ret = Point(
808 						(arc_radius.x * cos(arc_angle.x)) + arc_center.x,
809 						(arc_radius.y * cos(arc_angle.y)) + arc_center.y,
810 						(arc_radius.z * cos(arc_angle.z)) + arc_center.z);
811 					
812 					arc_angle.x += arc_delta.x;
813 					arc_angle.y += arc_delta.y;
814 					arc_angle.z += arc_delta.z;
815 					
816 					if (arc_angle.x >= arc_maxangle.x)
817 						ret.x = location.x;
818 					if (arc_angle.y >= arc_maxangle.y)
819 						ret.y = location.y;
820 					if (arc_angle.z >= arc_maxangle.z)
821 						ret.z = location.z;
822 					
823 					return ret;
824 					
825 				case Type.Bezier:
826 					import std.math : ceil;
827 					
828 					if (bezier_bezierOffset >= 1f)
829 						return Point.init;
830 					
831 					Point ret;
832 					
833 					//
834 					
835 					bezier_buffer[0] = origin_point.x;
836 					bezier_buffer[$-1] = location.x;
837 					foreach(i, p; bezier_points)
838 						bezier_buffer[1+i] = p.x;
839 					
840 					bezier_handle(bezier_buffer.length, bezier_buffer, bezier_bezierOffset);
841 					ret.x = bezier_buffer[0];
842 					
843 					//
844 					
845 					bezier_buffer[0] = origin_point.y;
846 					bezier_buffer[$-1] = location.y;
847 					foreach(i, p; bezier_points)
848 						bezier_buffer[1+i] = p.y;
849 					
850 					bezier_handle(bezier_buffer.length, bezier_buffer, bezier_bezierOffset);
851 					ret.y = bezier_buffer[0];
852 					
853 					//
854 					
855 					bezier_buffer[0] = origin_point.z;
856 					bezier_buffer[$-1] = location.z;
857 					foreach(i, p; bezier_points)
858 						bezier_buffer[1+i] = p.z;
859 					
860 					bezier_handle(bezier_buffer.length, bezier_buffer, bezier_bezierOffset);
861 					ret.z = bezier_buffer[0];
862 					
863 					///
864 					
865 					bezier_bezierOffset += bezier_tick;
866 					
867 					if (bezier_bezierOffset >= 1f) {
868 						ret.x = location.x;
869 						ret.y = location.y;
870 						ret.z = location.z;
871 					}
872 					return ret;
873 					
874 				default:
875 					return Point.init;
876 			}
877 		}
878 		
879 		enum Type {
880 			Error,
881 			Point,
882 			LineTo,
883 			Bezier,
884 			Arc
885 		}
886 	}
887 	
888 	double bezier_combine(double a, double b, double t) {
889 		return (a*(1-t)) + (b*t);
890 	}
891 	
892 	void bezier_handle(size_t recurseIndex, double[] bezier_buffer, double bezier_bezierOffset) {
893 		while(recurseIndex > 1) {
894 			uint newIndex;
895 			foreach(actualIndex; 1 .. recurseIndex) {
896 				double
897 					a = bezier_buffer[actualIndex-1],
898 					b = bezier_buffer[actualIndex];
899 				bezier_buffer[newIndex] = bezier_combine(a, b, bezier_bezierOffset);
900 				newIndex++;
901 			}
902 			
903 			recurseIndex = newIndex;
904 		}
905 	}
906 	
907 	bool train_NN(ref double cangle, ref double error, double train_rate, double radius, double center, double v) {
908 		import std.math : cos, PI, abs;
909 		
910 		error = (radius*cos(cangle)+center)-v;
911 		cangle += error*train_rate;
912 		return abs(error) > train_rate;
913 	}
914 	
915 	double calculateDifferenceOnAxis(double a, double b) {
916 		import std.math : abs, copysign;
917 		
918 		if (a >= 0 && b >= 0) {
919 		} else if (a <= 0 && b <= 0) {
920 		} else if (a > b) {
921 			return abs(b)+a;
922 		} else {
923 			return -(abs(a)+b);
924 		}
925 		
926 		return a-b;
927 	}
928 }